package com.xf.common.security.starter.configure;

import com.xf.common.core.entity.FebsAuthUser;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.List;
import java.util.Map;

/**
 * 本地鉴权服务,微服务使用
 * 网关鉴权待更新
 *
 * @author xufeng
 */
@Slf4j
public class LocalResourceServerTokenServices implements ResourceServerTokenServices {

    protected final Log logger = LogFactory.getLog(this.getClass());

    private final TokenStore tokenStore;


    public LocalResourceServerTokenServices(JwtAccessTokenConverter accessTokenConverter) {
        this.tokenStore = new JwtTokenStore(accessTokenConverter);
    }

    /**
     * 从指定的令牌字符串中抽取认证信息, 构建 OAuth2Authentication 对象.
     * 使用网关鉴权 需要实现这个ReactiveAuthorizationManager
     *
     * @param accessToken
     * @return
     * @throws AuthenticationException
     * @throws InvalidTokenException
     */
    @SneakyThrows(value = {Exception.class})
    @Override
    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
        if (oAuth2Authentication == null) {
            return null;
        }
        //已经鉴权过了
        if ((oAuth2Authentication.getPrincipal() instanceof FebsAuthUser)) {
            return oAuth2Authentication;
        }
        OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
        //组装权限列表
        List<GrantedAuthority> grantedAuthorities = (List<GrantedAuthority>) oAuth2Authentication.getAuthorities();
        Map<String, Object> info = oAuth2AccessToken.getAdditionalInformation();
        Map<String, Object> userInfo = (Map) info.get("userInfo");
        FebsAuthUser authUser = new FebsAuthUser(info.get("user_name").toString(), "N/A", true, true, true, false,
                grantedAuthorities);
        if (userInfo != null) {
            BeanUtils.populate(authUser, userInfo);
        }
        Authentication userAuthentication = new UsernamePasswordAuthenticationToken(authUser, "N/A", grantedAuthorities);
        OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication);
        authentication.setAuthenticated(true);
        return authentication;
    }

    /**
     * 仅用于 CheckTokenEndpoint 端点, 后者用于在授权服务器接收资源服务器的请求校验令牌. 所以对于资源服务器来说, 并不需要实现它.
     *
     * @param accessToken
     * @return
     */
    @Override
    public OAuth2AccessToken readAccessToken(String accessToken) {
        log.debug("CustomResourceServerTokenServices :: readAccessToken called ...");
        throw new UnsupportedOperationException("暂不支持 readAccessToken!");
    }


}
